// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//


#pragma once

#include <pollux/type/simple_function_api.h>
#include <pollux/type/type.h>

namespace kumo::pollux {
    namespace exec {
        class CastOperator;
    }

    // Given a class T, create a custom type that can be used to refer to it, with
    // an underlying opaque vector type.
    // Note that name must be stored in a variable and passed and cant be inlined.
    // OpaqueCustomTypeRegister<T, "type"> wont compile.
    // but static constexpr char* type = "type", OpaqueCustomTypeRegister<T, type>
    // works.
    template<typename T, const char * customTypeName>
    class OpaqueCustomTypeRegister {
    public:
        static bool registerType() {
            return kumo::pollux::registerCustomType(
                customTypeName, std::make_unique<const TypeFactory>());
        }

        static bool unregisterType() {
            return kumo::pollux::unregisterCustomType(customTypeName);
        }

        // Type used in the simple function interface as CustomType<TypeT>.
        struct TypeT {
            using type = std::shared_ptr<T>;
            static constexpr const char *typeName = customTypeName;
        };

        using SimpleType = CustomType<TypeT>;

        class PolluxType : public OpaqueType {
        public:
            PolluxType() : OpaqueType(std::type_index(typeid(T))) {
            }

            static const TypePtr &get() {
                static const TypePtr instance{new PolluxType()};
                return instance;
            }

            static const std::shared_ptr<const exec::CastOperator> &getCastOperator() {
                POLLUX_UNSUPPORTED();
            }

            bool equivalent(const pollux::Type &other) const override {
                // Pointer comparison works since this type is a singleton.
                return this == &other;
            }

            const char *name() const override {
                return customTypeName;
            }

            std::string toString() const override {
                return customTypeName;
            }
        };

        static const TypePtr &singletonTypePtr() {
            return PolluxType::get();
        }

    private:
        class TypeFactory : public CustomTypeFactories {
        public:
            TypeFactory() = default;

            TypePtr getType(
                const std::vector<TypeParameter> &parameters) const override {
                POLLUX_CHECK(parameters.empty());
                return singletonTypePtr();
            }

            exec::CastOperatorPtr getCastOperator() const override {
                POLLUX_UNSUPPORTED();
            }

            AbstractInputGeneratorPtr getInputGenerator(
                const InputGeneratorConfig & /*config*/) const override {
                return nullptr;
            }
        };
    };
} // namespace kumo::pollux
